<!DOCTYPE html>
<html>
<!--
Copyright 2008 The Closure Library Authors. All Rights Reserved.

Use of this source code is governed by the Apache License, Version 2.0.
See the COPYING file for details.
-->
<!--

  @author earmbrust@google.com (Erick Armbrust)
-->
<head>
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<title>Closure Unit Tests - goog.testing.mockmatchers</title>
<script src="../base.js"></script>
<script>
  goog.require('goog.dom');
  goog.require('goog.testing.FunctionMock');
  goog.require('goog.testing.mockmatchers');
  goog.require('goog.testing.jsunit');
</script>
</head>
<body>

<div id="someDiv">
</div>

<script>
  // A local reference to the mockmatchers namespace.
  var matchers = goog.testing.mockmatchers;

  // Simple classes to test the InstanceOf matcher.
  var foo = function() {};
  var bar = function() {};

  // Simple class to test adding error messages to
  // MockExpectation objects
  function MockMock() {
    this.errorMessages = [];
  }

  var mockExpect = null;

  MockMock.prototype.addErrorMessage = function(msg) {
    this.errorMessages.push(msg);
  };


  MockMock.prototype.getErrorMessageCount = function() {
    return this.errorMessages.length;
  };


  function setUp() {
    mockExpect = new MockMock();
  }


  function testNoMatchName() {
    // A matcher that does not fill in the match name
    var matcher = new goog.testing.mockmatchers.ArgumentMatcher(goog.isString);

    // Make sure the lack of match name doesn't affect the ability
    // to return True/False
    assertTrue(matcher.matches('hello'));
    assertFalse(matcher.matches(123));

    // Make sure we handle the lack of a match name
    assertFalse(matcher.matches(456, mockExpect));
    assertEquals(1, mockExpect.errorMessages.length);
    assertEquals("Expected: missing mockmatcher description " +
        "but was: <456> (Number)", mockExpect.errorMessages[0]);
  }


  function testInstanceOf() {
    var matcher = new matchers.InstanceOf(foo);
    assertTrue(matcher.matches(new foo()));
    assertFalse(matcher.matches(new bar()));

    assertFalse(matcher.matches(new bar(), mockExpect));
    assertEquals(1, mockExpect.errorMessages.length);
    assertEquals("Expected: instanceOf() " +
        "but was: <[object Object]> (Object)",
        mockExpect.errorMessages[0]);
  }


  function testTypeOf() {
    var matcher = new matchers.TypeOf('number');
    assertTrue(matcher.matches(1));
    assertTrue(matcher.matches(2));
    assertFalse(matcher.matches('test'));

    assertFalse(matcher.matches(true, mockExpect));
    assertEquals(1, mockExpect.errorMessages.length);
    assertEquals("Expected: typeOf(number) but was: <true> (Boolean)",
        mockExpect.errorMessages[0]);
  }


  function testRegexpMatch() {
    var matcher = new matchers.RegexpMatch(/^cho[dtp]/);
    assertTrue(matcher.matches('chodhop'));
    assertTrue(matcher.matches('chopper'));
    assertFalse(matcher.matches('chocolate'));
    assertFalse(matcher.matches(null));

    assertFalse(matcher.matches('an anger', mockExpect));
    assertEquals(1, mockExpect.errorMessages.length);
    assertEquals('Expected: match(/^cho[dtp]/) but was: <an anger> (String)',
        mockExpect.errorMessages[0]);
  }


  function testObjectEquals() {
    // Test a simple match.
    var simpleMatcher = new matchers.ObjectEquals({name: 'Bob', age: 42});
    assertTrue(simpleMatcher.matches({name: 'Bob', age: 42}, mockExpect));
    assertEquals(0, mockExpect.getErrorMessageCount());
    expectObjectEqualsFailure(simpleMatcher, {name: 'Bill', age: 42},
        'name: Expected <Bob> (String) but was <Bill> (String)');
    expectObjectEqualsFailure(simpleMatcher, {name: 'Bob', age: 21},
        'age: Expected <42> (Number) but was <21> (Number)');
    expectObjectEqualsFailure(simpleMatcher, {name: 'Bob'},
        'property age not present in actual Object');
    expectObjectEqualsFailure(simpleMatcher,
        {name: 'Bob', age: 42, country: 'USA'},
        'property country not present in expected Object');

    // Multiple mismatches should include multiple messages.
    expectObjectEqualsFailure(simpleMatcher, {name: 'Jim', age: 36},
        'name: Expected <Bob> (String) but was <Jim> (String)\n' +
        '   age: Expected <42> (Number) but was <36> (Number)');
  }

  function testComplexObjectEquals() {
    var complexMatcher = new matchers.ObjectEquals(
        {a: 'foo', b: 2, c: ['bar', 3], d: {sub1: 'baz', sub2: -1}});
    assertTrue(complexMatcher.matches(
        {a: 'foo', b: 2, c: ['bar', 3], d: {sub1: 'baz', sub2: -1}}));
    expectObjectEqualsFailure(complexMatcher,
        {a: 'foo', b: 2, c: ['bar', 3], d: {sub1: 'zap', sub2: -1}},
        'sub1: Expected <baz> (String) but was <zap> (String)');
    expectObjectEqualsFailure(complexMatcher,
        {a: 'foo', b: 2, c: ['bar', 6], d: {sub1: 'baz', sub2: -1}},
        'c[1]: Expected <3> (Number) but was <6> (Number)');
  }


  function testSaveArgument() {
    var saveMatcher = new matchers.SaveArgument();
    assertTrue(saveMatcher.matches(42));
    assertEquals(42, saveMatcher.arg);

    saveMatcher = new matchers.SaveArgument(goog.isString);
    assertTrue(saveMatcher.matches('test'));
    assertEquals('test', saveMatcher.arg);
    assertFalse(saveMatcher.matches(17));
    assertEquals(17, saveMatcher.arg);

    saveMatcher = new matchers.SaveArgument(new matchers.ObjectEquals({
      value: 'value'
    }));
    assertTrue(saveMatcher.matches({ value: 'value' }));
    assertEquals('value', saveMatcher.arg.value);
    assertFalse(saveMatcher.matches('test'));
    assertEquals('test', saveMatcher.arg);
  }


  function testIsArray() {
    assertTrue(matchers.isArray.matches([]));
    assertTrue(matchers.isArray.matches(new Array()));
    assertFalse(matchers.isArray.matches('test'));

    assertFalse(matchers.isArray.matches({}, mockExpect));
    assertEquals(1, mockExpect.errorMessages.length);
    assertEquals("Expected: isArray but was: <[object Object]> (Object)",
        mockExpect.errorMessages[0]);
  }


  function testIsArrayLike() {
    var nodeList = (function() {
      var div = document.createElement('div');
      div.appendChild(document.createElement('p'));
      div.appendChild(document.createElement('p'));
      return div.getElementsByTagName('div');
    })();

    assertTrue(matchers.isArrayLike.matches([]));
    assertTrue(matchers.isArrayLike.matches(new Array()));
    assertTrue(matchers.isArrayLike.matches(nodeList));
    assertFalse(matchers.isArrayLike.matches('test'));

    assertFalse(matchers.isArrayLike.matches(3, mockExpect));
    assertEquals(1, mockExpect.errorMessages.length);
    assertEquals("Expected: isArrayLike but was: <3> (Number)",
        mockExpect.errorMessages[0]);
  }


  function testIsDateLike() {
    assertTrue(matchers.isDateLike.matches(new Date()));
    assertFalse(matchers.isDateLike.matches('test'));

    assertFalse(matchers.isDateLike.matches('test', mockExpect));
    assertEquals(1, mockExpect.errorMessages.length);
    assertEquals("Expected: isDateLike but was: <test> (String)",
        mockExpect.errorMessages[0]);
  }


  function testIsString() {
    assertTrue(matchers.isString.matches('a'));
    assertTrue(matchers.isString.matches('b'));
    assertFalse(matchers.isString.matches(null));

    assertFalse(matchers.isString.matches(null, mockExpect));
    assertEquals(1, mockExpect.errorMessages.length);
    assertEquals("Expected: isString but was: <null>",
        mockExpect.errorMessages[0]);
  }


  function testIsBoolean() {
    assertTrue(matchers.isBoolean.matches(true));
    assertTrue(matchers.isBoolean.matches(false));
    assertFalse(matchers.isBoolean.matches(null));

    assertFalse(matchers.isBoolean.matches([], mockExpect));
    assertEquals(1, mockExpect.errorMessages.length);
    assertEquals("Expected: isBoolean but was: <> (Array)",
        mockExpect.errorMessages[0]);
  }


  function testIsNumber() {
    assertTrue(matchers.isNumber.matches(-1));
    assertTrue(matchers.isNumber.matches(1));
    assertTrue(matchers.isNumber.matches(1.25));
    assertFalse(matchers.isNumber.matches(null));

    assertFalse(matchers.isNumber.matches('hello', mockExpect));
    assertEquals(1, mockExpect.errorMessages.length);
    assertEquals("Expected: isNumber but was: <hello> (String)",
        mockExpect.errorMessages[0]);
  }


  function testIsFunction() {
    assertTrue(matchers.isFunction.matches(function() {}));
    assertFalse(matchers.isFunction.matches('test'));

    assertFalse(matchers.isFunction.matches({}, mockExpect));
    assertEquals(1, mockExpect.errorMessages.length);
    assertEquals("Expected: isFunction but was: <[object Object]> (Object)",
        mockExpect.errorMessages[0]);
  }


  function testIsObject() {
    assertTrue(matchers.isObject.matches({}));
    assertTrue(matchers.isObject.matches(new Object()));
    assertTrue(matchers.isObject.matches(new function() {}));
    assertTrue(matchers.isObject.matches([]));
    assertTrue(matchers.isObject.matches(new Array()));
    assertTrue(matchers.isObject.matches(function() {}));
    assertFalse(matchers.isObject.matches(null));

    assertFalse(matchers.isObject.matches(1234, mockExpect));
    assertEquals(1, mockExpect.errorMessages.length);
    assertEquals("Expected: isObject but was: <1234> (Number)",
        mockExpect.errorMessages[0]);
  }


  function testIsNodeLike() {
    assertFalse(matchers.isNodeLike.matches({}));
    assertFalse(matchers.isNodeLike.matches(1));
    assertFalse(matchers.isNodeLike.matches(function() {}));
    assertFalse(matchers.isNodeLike.matches(false));
    assertTrue(matchers.isNodeLike.matches(document.body));
    assertTrue(matchers.isNodeLike.matches(goog.dom.getElement('someDiv')));

    assertFalse(matchers.isNodeLike.matches('test', mockExpect));
    assertEquals(1, mockExpect.errorMessages.length);
    assertEquals("Expected: isNodeLike but was: <test> (String)",
        mockExpect.errorMessages[0]);
  }


  function testIgnoreArgumentsMatcher() {
    // ignoreArgument always returns true:
    assertTrue(matchers.ignoreArgument.matches());
    assertTrue(matchers.ignoreArgument.matches(356));
    assertTrue(matchers.ignoreArgument.matches('str'));
    assertTrue(matchers.ignoreArgument.matches(['array', 123, false]));
    assertTrue(matchers.ignoreArgument.matches({'map': 1, key2: 'value2'}));
  }


  function testFlexibleArrayMatcher() {
    // Test that basic lists are verified properly.
    var a1 = [1, 'test'];
    var a2 = [1, 'test'];
    var a3 = [1, 'test', 'extra'];
    assertTrue(matchers.flexibleArrayMatcher(a1, a2));
    assertFalse(matchers.flexibleArrayMatcher(a1, a3));

    // Test that basic lists with basic class instances are verified properly.
    var instance = new foo();
    a1 = [1, 'test', instance];
    a2 = [1, 'test', instance];
    a3 = [1, 'test', new foo()];
    assertTrue(matchers.flexibleArrayMatcher(a1, a2));
    assertTrue(matchers.flexibleArrayMatcher(a1, a3));

    // Create an argument verifier that returns a consistent value.
    var verifyValue = true;
    var argVerifier = function() {};
    goog.inherits(argVerifier, matchers.ArgumentMatcher);
    argVerifier.prototype.matches = function(arg) {
      return verifyValue;
    };

    // Test that the arguments are always verified when the verifier returns
    // true.
    a1 = [1, 'test', new argVerifier()];
    a2 = [1, 'test', 'anything'];
    a3 = [1, 'test', 12345];
    assertTrue(matchers.flexibleArrayMatcher(a1, a2));
    assertTrue(matchers.flexibleArrayMatcher(a1, a3));

    // Now test the case when then verifier returns false.
    verifyValue = false;
    assertFalse(matchers.flexibleArrayMatcher(a1, a2));
    assertFalse(matchers.flexibleArrayMatcher(a1, a3));

    // And test we report errors back up via the opt_expectation
    assertFalse(matchers.flexibleArrayMatcher(a2, a3, mockExpect));
    assertEquals(1, mockExpect.errorMessages.length);
    assertEquals(
        'Expected <anything> (String) but was <12345> (Number)\n' +
        '    Expected <anything> (String) but was <12345> (Number)',
        mockExpect.errorMessages[0]);

    // And test we report errors found via the matcher
    a1 = [1, goog.testing.mockmatchers.isString ];
    a2 = [1, 'test string'];
    a3 = [1, null];
    assertTrue(matchers.flexibleArrayMatcher(a1, a2, mockExpect));
    assertFalse(matchers.flexibleArrayMatcher(a1, a3, mockExpect));
    // Old error is still there
    assertEquals(2, mockExpect.errorMessages.length);
    assertEquals(
        'Expected <anything> (String) but was <12345> (Number)\n' +
        '    Expected <anything> (String) but was <12345> (Number)',
        mockExpect.errorMessages[0]);
    // plus the new error...
    assertEquals("Expected: isString but was: <null>",
        mockExpect.errorMessages[1]);
  }


  /**
   * Utility method for checking for an ObjectEquals match failure.  Checks that
   * the expected error message was included in the error messages appended to
   * the expectation object.
   * @param {goog.testing.mockmatchers.ArgumentMatcher.ObjectEquals} matcher
   *     The matcher to test against.
   * @param {Object} matchObject The object to compare.
   * @param {string=} opt_errorMsg The deep object comparison failure message
   *     to check for.
   */
  function expectObjectEqualsFailure(matcher, matchObject, opt_errorMsg) {
    mockExpect.errorMessages = [];
    assertFalse(matcher.matches(matchObject, mockExpect));
    assertNotEquals(0, mockExpect.getErrorMessageCount());
    if (opt_errorMsg) {
      assertContains(opt_errorMsg, mockExpect.errorMessages[0]);
    }
  }
</script>
</body>
</html>
